﻿Imports Microsoft.Office.Interop

Partial Class pages_files_Duplicates
    Inherits CPage


    Protected Sub pages_files_Duplicates_Init(sender As Object, e As System.EventArgs) Handles Me.Init



        MenuSelected = "Files"
        AddMenuSide("Files", CSitemap.Files)
        AddMenuSide("Duplicates")
        AddMenuSide("Missing", CSitemap.FilesMissing)





        Dim files As List(Of String) = GetPublicFiles()
        Dim duplicates As Dictionary(Of String, List(Of String)) = GetDuplicates(files)


        Dim dict As Dictionary(Of String, CFile) = CFile.Cache.NamesDict

        For Each i As String In files
            Dim isNormal As Boolean = True

            Dim hash As String = FileHash(i)
            Dim matching As List(Of String) = duplicates(hash)

            Dim tr As New HtmlTableRow
            tbl.Rows.Add(tr)
            If tbl.Rows.Count Mod 2 = 0 Then tr.Attributes.Add("class", "alt_row")

            Dim td As New HtmlTableCell
            tr.Cells.Add(td)

            For Each j As String In matching
                Dim name As String = j.Trim
                j = j.ToLower.Trim

                td.NoWrap = True
                Dim div As New HtmlGenericControl("div")
                td.Controls.Add(div)

                If Not dict.ContainsKey(j) Then
                    Dim hasAnotherVersionInUse As Boolean = False
                    If matching.Count > 1 Then
                        For Each k As String In matching
                            k = k.ToLower.Trim.Replace("\", "/")
                            If dict.ContainsKey(k) Then
                                hasAnotherVersionInUse = True
                                Exit For
                            End If
                        Next
                    End If

                    isNormal = False

                    Dim lbl As New Label
                    div.Controls.Add(lbl)
                    lbl.Text = "Unused!"
                    lbl.ForeColor = Drawing.Color.Red

                    If Not hasAnotherVersionInUse Then
                        'Unused file: Create/Find sermon or archive it
                        Dim add As New HyperLink
                        add.ImageUrl = "~/images/add.png"
                        add.NavigateUrl = "~/pages/files/Create.aspx?name=" & CSitemap.Encode(name)
                        div.Controls.Add(add)
                        add.Style.Add("margin-left", "10px")

                        Dim del As New ImageButton
                        CWeb.OnClickConfirm(del, "Archive this file outside of public?")
                        del.ImageUrl = "~/images/delete.png"
                        del.CommandArgument = j
                        del.ID = String.Concat("s_", j.GetHashCode, "_", i.GetHashCode)
                        AddHandler del.Click, AddressOf btnArchive_Click
                        div.Controls.Add(del)
                        del.Style.Add("margin-left", "10px")
                        del.ToolTip = "Archive this file"
                    Else
                        'Redundant file: remove it
                        Dim del As New ImageButton
                        CWeb.OnClickConfirm(del, "Permanently delete this file?")
                        del.ImageUrl = "~/images/delete_red.png"
                        del.CommandArgument = j
                        del.ID = String.Concat("s_", j.GetHashCode, "_", i.GetHashCode)
                        AddHandler del.Click, AddressOf btnDelete_Click
                        div.Controls.Add(del)
                        del.Style.Add("margin-left", "10px")
                        del.Style.Add("margin-right", "10px")
                        del.ToolTip = "Delete this file"
                    End If
                Else
                    Dim lnk As New HyperLink
                    If div.Controls.Count > 0 Then
                        Dim lit As New Literal
                        lit.Text = ", "
                        div.Controls.Add(lit)
                    End If
                    div.Controls.Add(lnk)
                    With dict(j)
                        lnk.Text = String.Concat("#", .FileProductId, ":", .Product.ProductName)
                        lnk.NavigateUrl = CSitemap.ProductEdit(.FileProductId)
                    End With
                End If
            Next


            td = New HtmlTableCell
            tr.Cells.Add(td)
            td.InnerText = i
            td.NoWrap = True


            td = New HtmlTableCell
            tr.Cells.Add(td)

            If matching.Count > 1 Then
                isNormal = False

                For Each j As String In matching
                    td.NoWrap = True
                    Dim div As New HtmlGenericControl("div")
                    td.Controls.Add(div)
                    Dim lbl As New Label
                    div.Controls.Add(lbl)
                    lbl.Text = j
                Next
            End If

            tr.Visible = Not isNormal OrElse chkShowAll.Checked
        Next

    End Sub

    'List files recursively
    Private Shared Function GetPublicFiles() As List(Of String)
        Dim list As New List(Of String)
        GetPublicFiles(CConfig.DropboxPublic, list)
        Return list
    End Function
    Private Shared Sub GetPublicFiles(folderPath As String, list As List(Of String))
        For Each i As String In IO.Directory.GetDirectories(folderPath)
            If i.ToLower.EndsWith("_files") Then Continue For
            GetPublicFiles(i, list)
        Next
        For Each i As String In IO.Directory.GetFiles(folderPath)
            Dim s As String = i.Substring(CConfig.DropboxPublic.Length)
            list.Add(s)
        Next
    End Sub

    'Get/Store the hash-code for each file
    Private Shared Function FileHash(path As String) As String
        path = path.ToLower.Trim
        With FileHashing
            If .ContainsKey(path) Then Return .Item(path)

            Dim filePath As String = String.Concat(CConfig.DropboxPublic, path)
            Dim hash As String = CBinary.Sha512Base64(ASCIIEncoding.ASCII.GetBytes(filePath)) 'for 100mb+ files, just hash the name
            If New IO.FileInfo(filePath).Length < 100000000 Then
                hash = CBinary.Sha512Base64(IO.File.ReadAllBytes(filePath)) 'normally hash the contents
            End If
            .Item(path) = hash

            'Persist it
            filePath = HttpContext.Current.Server.MapPath("~/app_data/hashing.txt")
            If Not IO.File.Exists(filePath) Then
                Dim sb As New Text.StringBuilder
                For Each i As String In FileHashing.Keys
                    sb.Append(i).Append(vbTab).AppendLine(FileHashing(i))
                Next
                IO.File.WriteAllText(filePath, sb.ToString)
            Else
                IO.File.AppendAllText(filePath, String.Concat(path, vbTab, .Item(path), vbCrLf))
            End If
            Return hash
        End With
    End Function
    Private Shared ReadOnly Property FileHashing As Dictionary(Of String, String)
        Get
            Dim dict As Dictionary(Of String, String) = CApplication.Get("FileHashing")
            If IsNothing(dict) Then
                dict = New Dictionary(Of String, String)
                CApplication.Set("FileHashing", dict)

                'restore it
                Dim filePath As String = HttpContext.Current.Server.MapPath("~/app_data/hashing.txt")
                If IO.File.Exists(filePath) Then
                    For Each line As String In IO.File.ReadAllLines(filePath)
                        Try
                            Dim ss As String() = line.Split(vbTab)
                            If ss.Length > 1 Then dict(ss(0)) = ss(1)
                        Catch
                        End Try
                    Next
                End If

            End If
            Return dict
        End Get
    End Property

    'Compute duplicates
    Private Shared Function GetDuplicates(paths As List(Of String)) As Dictionary(Of String, List(Of String))
        Dim dict As New Dictionary(Of String, List(Of String))
        For Each path As String In paths
            Dim hash As String = FileHash(path)
            Dim list As List(Of String) = Nothing
            If Not dict.TryGetValue(hash, list) Then
                list = New List(Of String)(1)
                dict(hash) = list
            End If
            list.Add(path)
        Next
        Return dict
    End Function


    Private Sub btnArchive_Click(sender As ImageButton, e As ImageClickEventArgs)
        Dim name As String = sender.CommandArgument
        Dim oldPath As String = CConfig.DropboxPublic & name
        Dim newPath As String = oldPath.Replace("public", "archive")
        Dim newDir As String = IO.Path.GetDirectoryName(newPath)
        If Not IO.Directory.Exists(newDir) Then IO.Directory.CreateDirectory(newDir)
        IO.File.Move(oldPath, newPath)
        Response.Redirect(Request.RawUrl)
    End Sub

    Private Sub btnDelete_Click(sender As ImageButton, e As ImageClickEventArgs)
        Dim name As String = sender.CommandArgument
        Dim oldPath As String = CConfig.DropboxPublic & name
        If IO.File.Exists(oldPath) Then IO.File.Delete(oldPath)
        Response.Redirect(Request.RawUrl)
    End Sub

End Class
